/**
 * Grep Query Tool - Main implementation for GitHub code search
 * Integrates all components to provide complete search functionality
 */

import { 
  SearchParams, 
  FormattedResponse,
  FormattedSearchResult
} from '../types.js';
import { ParameterValidator, InputProcessor } from '../utils/validation.js';
import { GrepAPIClient } from '../utils/api.js';
import { FormattingUtils } from '../utils/formatting.js';

/**
 * Internal search result format for processing
 */
interface InternalSearchResult {
  repository: string;
  file_path: string;
  line_number: number;
  code_snippet: string;
  repository_url: string;
  file_url: string;
  context_before?: string[];
  context_after?: string[];
}

/**
 * Main grep query tool implementation
 * Integrates all components to provide complete GitHub code search functionality
 */
export class GrepQueryTool {
  private apiClient: GrepAPIClient;

  constructor() {
    this.apiClient = new GrepAPIClient();
  }

  /**
   * Execute grep query with full validation and formatting
   */
  async executeQuery(params: SearchParams): Promise<string> {
    try {
      // Step 1: Validate input parameters
      const validationResult = ParameterValidator.validateSearchParams(params);
      if (!validationResult.isValid) {
        return this.formatErrorResponse(validationResult.error || 'Invalid parameters');
      }

      // Step 2: Process and clean input parameters
      const processor = new InputProcessor();
      const processedParams = processor.processSearchParams(params);

      // Step 3: Execute API search
      const searchResponse = await this.apiClient.search(processedParams);

      // Step 4: Transform API response to internal format
      const searchResults = this.transformAPIResponse(searchResponse);

      // Step 5: Format and clean results
      const formattedResults = await this.formatSearchResults(searchResults);

      // Step 6: Generate final formatted response
      const finalResponse = this.generateFormattedResponse(processedParams, formattedResults);

      return JSON.stringify(finalResponse, null, 2);

    } catch (error) {
      return this.handleError(error);
    }
  }

  /**
   * Transform API response to internal search result format
   */
  private transformAPIResponse(apiResponse: any): InternalSearchResult[] {
    if (!apiResponse?.hits?.hits || !Array.isArray(apiResponse.hits.hits)) {
      return [];
    }

    return apiResponse.hits.hits.map((hit: any) => ({
      repository: hit.repo?.raw || 'unknown',
      file_path: hit.path?.raw || 'unknown',
      line_number: this.extractLineNumber(hit.content?.snippet || ''),
      code_snippet: hit.content?.snippet || '',
      repository_url: `https://github.com/${hit.repo?.raw || ''}`,
      file_url: `https://github.com/${hit.repo?.raw || ''}/blob/${hit.branch?.raw || 'main'}/${hit.path?.raw || ''}`,
      context_before: [],
      context_after: []
    }));
  }

  /**
   * Format search results with HTML cleaning and syntax highlighting
   */
  private async formatSearchResults(results: InternalSearchResult[]): Promise<FormattedSearchResult[]> {
    const formatted: FormattedSearchResult[] = [];

    for (const result of results) {
      try {
        // Clean HTML content
        const cleanedSnippet = FormattingUtils.extractTextFromHtml(result.code_snippet);
        
        // Extract language from file path
        const language = FormattingUtils.getLanguageFromPath(result.file_path);
        
        // Format code with syntax highlighting
        const formattedSnippet = FormattingUtils.formatCodeSnippet(cleanedSnippet, language);

        formatted.push({
          repository: result.repository,
          branch: 'main', // Default branch
          filePath: result.file_path,
          language: language,
          lineNumbers: [result.line_number],
          totalMatches: 1,
          snippet: formattedSnippet,
          cleanContent: cleanedSnippet,
          url: result.file_url,
          contextLines: 3
        });
      } catch (error) {
        // If formatting fails, use cleaned version
        formatted.push({
          repository: result.repository,
          branch: 'main',
          filePath: result.file_path,
          language: FormattingUtils.getLanguageFromPath(result.file_path),
          lineNumbers: [result.line_number],
          totalMatches: 1,
          snippet: result.code_snippet,
          cleanContent: FormattingUtils.extractTextFromHtml(result.code_snippet),
          url: result.file_url,
          contextLines: 3
        });
      }
    }

    return formatted;
  }

  /**
   * Generate final formatted response
   */
  private generateFormattedResponse(params: SearchParams, results: FormattedSearchResult[]): FormattedResponse {
    // Group results by repository
    const repositoryGroups = this.groupResultsByRepository(results);
    
    // Calculate statistics
    const totalResults = results.length;
    const repositoriesFound = repositoryGroups.length;
    const languageStats = this.calculateLanguageStats(results);
    const repositoryStats = this.calculateRepositoryStats(results);

    return {
      query: params.query,
      summary: {
        total_results: totalResults,
        results_shown: Math.min(totalResults, 10), // Limit display
        repositories_found: repositoriesFound,
        top_languages: languageStats.slice(0, 5),
        top_repositories: repositoryStats.slice(0, 5)
      },
      results_by_repository: repositoryGroups.slice(0, 10) // Limit to top 10 repositories
    };
  }

  /**
   * Group results by repository
   */
  private groupResultsByRepository(results: FormattedSearchResult[]): any[] {
    const groups = new Map();

    for (const result of results) {
      const repoName = result.repository;
      if (!groups.has(repoName)) {
        groups.set(repoName, {
          repository: repoName,
          matches_count: 0,
          files: []
        });
      }

      const group = groups.get(repoName);
      group.matches_count += 1;
      group.files.push({
        file_path: result.filePath,
        branch: result.branch,
        total_matches: result.totalMatches,
        line_numbers: result.lineNumbers,
        language: result.language,
        code_snippet: result.cleanContent
      });
    }

    return Array.from(groups.values());
  }

  /**
   * Calculate language statistics
   */
  private calculateLanguageStats(results: FormattedSearchResult[]): any[] {
    const languageCounts = new Map();

    for (const result of results) {
      const language = result.language;
      languageCounts.set(language, (languageCounts.get(language) || 0) + 1);
    }

    return Array.from(languageCounts.entries())
      .map(([language, count]) => ({ language, count }))
      .sort((a, b) => b.count - a.count);
  }

  /**
   * Calculate repository statistics
   */
  private calculateRepositoryStats(results: FormattedSearchResult[]): any[] {
    const repoCounts = new Map();

    for (const result of results) {
      const repo = result.repository;
      repoCounts.set(repo, (repoCounts.get(repo) || 0) + 1);
    }

    return Array.from(repoCounts.entries())
      .map(([repository, count]) => ({ repository, count }))
      .sort((a, b) => b.count - a.count);
  }

  /**
   * Extract line number from code snippet
   */
  private extractLineNumber(snippet: string): number {
    // Try to extract line number from snippet context
    const lineMatch = snippet.match(/^(\d+):/);
    return lineMatch ? parseInt(lineMatch[1], 10) : 1;
  }


  /**
   * Format error response
   */
  private formatErrorResponse(error: string): string {
    return JSON.stringify({
      error: true,
      message: error,
      timestamp: new Date().toISOString()
    }, null, 2);
  }

  /**
   * Handle errors during query execution
   */
  private handleError(error: any): string {
    console.error('Grep query error:', error);
    
    let errorMessage = 'An unexpected error occurred during the search';
    
    if (error.name === 'GrepAPITimeoutError') {
      errorMessage = 'Search request timed out. Please try again with a more specific query.';
    } else if (error.name === 'GrepAPIRateLimitError') {
      errorMessage = 'Rate limit exceeded. Please wait a moment before trying again.';
    } else if (error.name === 'GrepAPIError') {
      errorMessage = `Search API error: ${error.message}`;
    } else if (error.message) {
      errorMessage = error.message;
    }

    return this.formatErrorResponse(errorMessage);
  }
}

/**
 * Factory function to create and execute grep query
 */
export async function executeGrepQuery(params: SearchParams): Promise<string> {
  const tool = new GrepQueryTool();
  return await tool.executeQuery(params);
}